/**
 * Project Name:trustsql_sdk
 * File Name:TrustSDK.java
 * Package Name:com.tencent.trustsql.sdk
 * Date:Jul 26, 201710:30:31 AM
 * Copyright (c) 2017, Tencent All Rights Reserved.
 */

package com.mindata.blockchain.common;

import com.mindata.blockchain.block.PairKey;
import com.mindata.blockchain.common.algorithm.ECDSAAlgorithm;
import com.mindata.blockchain.common.exception.ErrorNum;
import com.mindata.blockchain.common.exception.TrustSDKException;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;

/**
 * ClassName:TrustSDK <br/>
 * Date: Jul 26, 2017 10:30:31 AM <br/>
 *
 * @author Rony
 * @since JDK 1.7
 */
public class TrustSDK {

    /**
     * generatePairKey:产生一对公私钥, 并返回. <br/>
     *
     * @return 返回公私钥对
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static PairKey generatePairKey() throws TrustSDKException {
        return generatePairKey(false);
    }

    /**
     * generatePairKey:生成私钥公钥对. <br/>
     *
     * @param encodePubKey 是否压缩
     * @return PairKey
     * @throws TrustSDKException TrustSDKException
     * @author ronyyang
     * @since JDK 1.7
     */
    public static PairKey generatePairKey(boolean encodePubKey) throws TrustSDKException {
        try {
            PairKey pair = new PairKey();
            String privateKey = ECDSAAlgorithm.generatePrivateKey();
            String pubKey = ECDSAAlgorithm.generatePublicKey(privateKey.trim(), encodePubKey);
            String address = ECDSAAlgorithm.getAddress(pubKey);
            pair.setPrivateKey(privateKey);
            pair.setPublicKey(pubKey);
            pair.setAddress(address);
            return pair;
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    /**
     * checkPairKey:验证一对公私钥是否匹配. <br/>
     *
     * @param prvKey 输入 存放私钥 长度必须为PRVKEY_DIGEST_LENGTH
     * @param pubKey 输入 存放公钥 长度必须为PUBKEY_DIGEST_LENGTH
     * @return true 公私钥匹配  false 公私钥不匹配
     * @throws TrustSDKException TrustSDKException
     * @author ronyyang
     * @since JDK 1.7
     */
    public static boolean checkPairKey(String prvKey, String pubKey) throws TrustSDKException {
        if (StringUtils.isEmpty(prvKey) || StringUtils.isEmpty(pubKey)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            String correctPubKey = ECDSAAlgorithm.generatePublicKey(prvKey.trim(), true);
            return pubKey.trim().equals(correctPubKey);
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    /**
     * generatePubkeyByPrvkey: 通过私钥计算相应公钥. <br/>
     *
     * @param privateKey 私钥字符串
     * @param encode     是否压缩公钥
     * @return 返回公钥字符串
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static String generatePubkeyByPrvkey(String privateKey, boolean encode) throws TrustSDKException {
        if (StringUtils.isEmpty(privateKey)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            return ECDSAAlgorithm.generatePublicKey(privateKey, encode);
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    /**
     * generatePubkeyByPrvkey: 通过私钥计算相应公钥. <br/>
     *
     * @param privateKey 私钥字符串
     * @return 返回公钥字符串
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static String generatePubkeyByPrvkey(String privateKey) throws TrustSDKException {
        return generatePubkeyByPrvkey(privateKey, false);
    }

    public static String decodePubkey(String encodePubKey) throws TrustSDKException {
        if (StringUtils.isEmpty(encodePubKey)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            return ECDSAAlgorithm.decodePublicKey(encodePubKey);
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    /**
     * generateAddrByPubkey:通过公钥获取对应地址. <br/>
     *
     * @param pubKey 公钥字符串
     * @return address
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static String generateAddrByPubkey(String pubKey) throws TrustSDKException {
        if (StringUtils.isEmpty(pubKey)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            return ECDSAAlgorithm.getAddress(Base64.decodeBase64(pubKey));
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    /**
     * generateAddrByPrvkey:通过私钥计算相应地址. <br/>
     *
     * @param privateKey 私钥字符串
     * @return Address
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static String generateAddrByPrvkey(String privateKey) throws TrustSDKException {
        if (StringUtils.isEmpty(privateKey)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            String pubKey = ECDSAAlgorithm.generatePublicKey(privateKey);
            return generateAddrByPubkey(pubKey);
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    /**
     * signString:为字符串进行签名, 并返回签名. <br/>
     *
     * @param privateKey 私钥字符串
     * @param data       需要签名的字符数组
     * @return 返回签名字符串
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static String signString(String privateKey, byte[] data) throws TrustSDKException {
        if (StringUtils.isEmpty(privateKey)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            return ECDSAAlgorithm.sign(privateKey, data);
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.SIGN_ERROR.getRetCode(), ErrorNum.SIGN_ERROR.getRetMsg(), e);
        }
    }

    public static String signString(String privateKey, String data) throws TrustSDKException, UnsupportedEncodingException {
        return signString(privateKey, data.getBytes("UTF-8"));
    }

    /**
     * verifyString:验证一个签名是否有效. <br/>
     *
     * @param pubKey    公钥字符串
     * @param srcString 源字符串
     * @param sign      签名字符串
     * @return 返回验证是否通过 true:验证成功 false:验证失败
     * @throws TrustSDKException TrustSDKException
     * @author Rony
     * @since JDK 1.7
     */
    public static boolean verifyString(String pubKey, String srcString, String sign) throws TrustSDKException {
        if (StringUtils.isEmpty(pubKey) || StringUtils.isEmpty(srcString) || StringUtils.isEmpty(sign)) {
            throw new TrustSDKException(ErrorNum.INVALID_PARAM_ERROR.getRetCode(), ErrorNum.INVALID_PARAM_ERROR.getRetMsg());
        }
        try {
            return ECDSAAlgorithm.verify(srcString, sign, pubKey);
        } catch (Exception e) {
            throw new TrustSDKException(ErrorNum.ECDSA_ENCRYPT_ERROR.getRetCode(), ErrorNum.ECDSA_ENCRYPT_ERROR.getRetMsg(), e);
        }
    }

    public static void main(String[] args) throws Exception {
        PairKey pairKey = TrustSDK.generatePairKey(true);
        String pubKey = pairKey.getPublicKey();
        String priKey = pairKey.getPrivateKey();
        System.out.println("pubKey:" + pubKey);
        System.out.println("priKey:" + priKey);

        String addrPub = TrustSDK.generateAddrByPubkey(pubKey);
        String addrPrv = TrustSDK.generateAddrByPrvkey(priKey);
        System.out.println(addrPub);
        System.out.println(addrPrv);

        System.out.println("checkPairKey:" + TrustSDK.checkPairKey(priKey, pubKey));
    }


}
